package com.serena.da.rest.services;

import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.message.BasicHeader;
import org.json.JSONObject;
import com.serena.da.rest.common.GlobalContext;
import com.serena.da.rest.http_helpers.GetRequest;
import com.serena.da.rest.http_helpers.PostRequest;
import com.serena.da.rest.http_helpers.PutRequest;
import com.serena.da.rest.http_helpers.Response;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Spitkovskyi Igor on 8/12/2014.
 */
public abstract class SraService {
    private SraServiceClient client;

/* Need to use client in multi-threading flow. Try information at http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html  ()
   Find section started with "While HttpClient instances are thread safe and can be shared between multiple threads of execution, it is highly recommended that each thread maintains its own dedicated instance of HttpContext"
   So, if I try using client as a singletone, I get exception.
*/

    protected SraService() throws Exception {
        client = SraServiceClient.getInstance();
    }

    protected SraService(String host, String port, String user, String pwd) {
        client = SraServiceClient.getInstance(host, port, user, pwd);
    }

    protected String getRestUrl(String FullServiceName, boolean useUrlAuthentication) {
        String restUrl = "";

        String sraGateUrl = "http://" + client.getHost() + ":" + client.getPort() + "/serena_ra";
        String getGlobalAppsUrl = "/rest/" + FullServiceName; //"/serena_ra/rest/deploy/globalEnvironment"; or "/serena_ra/rest/deploy/component"

        restUrl = sraGateUrl + getGlobalAppsUrl;

        if (useUrlAuthentication) {
            restUrl = restUrl + "?" + "username=" + client.getUser() + "&" + "password=" + client.getPassword();
        }

        return restUrl;
    }

    /**
     * For GET requests only
     */
    protected Response executeRequest(String url) throws Exception {
        return executeRequest(url, "GET", null, null, null);
    }

    /**
     * Overloaded methods for different types of PUT / POST requests
     */
    protected Response executeRequest(String url, String method, Object payload) throws Exception {
        return executeRequest(url, method, payload, null, null);
    }

    /**
     * For POST and PUT methods which create entity, which you do NOT want to store in the GlobalDictionary
     */
    protected Response executeRequest(String url, String method, Object payload, HashMap<String, String> Headers) throws Exception {
        return executeRequest(url, method, payload, Headers, null);
    }

    /**
     * For POST and PUT methods which do not require any custom headers (except authorization) to be added.
     */
    protected Response executeRequest(String url, String method, Object payload, String EntityNamePrefix) throws Exception {
        return executeRequest(url, method, payload, null, EntityNamePrefix);
    }

    /**
     * Following method combines actions directly related to sending request
     * 1. Sets JSON as PUT request entity.
     * 2. Passes entity-PREFIX to the "sendRequest()" method. Prefix is used for saving entity to GlobalContext
     * 3. Prints information about created entity to console. If no entity information returned is expected, it sends blank string value "".
     * 4. In case of success (no exception happens) it returns true;
     *
     * @param url              uri/url
     * @param method           POST, GET and PUT supported
     * @param payload          can be file, byte array (file content) or json string
     * @param headers          headers hash map: name-value
     * @param EntityNamePrefix Save ID of entity if entity-prefix has been provided.
     * @return Response object
     * @throws Exception
     */
    protected Response executeRequest(String url, String method, Object payload, HashMap<String, String> headers, String EntityNamePrefix) throws Exception {
        HttpRequestBase req;
        method = method.toUpperCase();

        switch (method) {
            case Method.GET:
                req = new GetRequest(url);
                break;

            case Method.PUT:
                req = new PutRequest(url);
                if (payload == null) break;

                if (payload instanceof String) {
                    ((PutRequest) req).setEntity((String) payload);
                }
                break;

            case Method.POST:
                req = new PostRequest(url);
                PostRequest request = (PostRequest) req;
                if (payload == null) break;

                if (payload instanceof File) {
                    request.setEntity((File) payload);
                } else if (payload instanceof byte[]) {
                    request.setEntity((byte[]) payload);
                } else if (payload instanceof String) {
                    request.setEntity((String) payload);
                }
                break;

            default:
                throw new Exception("Unknown http method type requested: " + method);

        }

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                req.setHeader(new BasicHeader(header.getKey(), header.getValue()));
            }
        }

        try {
            Response response = client.sendRequest(req);
            int statusCode = response.getStatusCode();

            if (statusCode == 200) {
                response.setSuccess(true);
                //Save ID of entity if entity-prefix has been provided.
                if (EntityNamePrefix != null) {
                    String storedInfo = SaveEntityIDtoGlobalContext("name", "id", response.getResponseBody(), EntityNamePrefix);
                    System.out.println("Following information has been stored to global context: " + storedInfo);
                }
            } else {
                response.setSuccess(false);
            }
            return response;
        } catch (Exception e) {
            System.out.println("Failed to perform request!");
            throw new Exception(e);
        }

        //printReturnedInformation(EntityDescription, response.getResponseBody());
    }

    protected String SaveEntityIDtoGlobalContext(String getKey, String getValue, String json, String entity_type_prefix) {
        JSONObject jsonObj = new JSONObject(json);
        if (getKey.isEmpty() || getValue.isEmpty()) {
            return "No key or value to be put into GlobalContext have been provided";
        }

        String key = entity_type_prefix + jsonObj.getString(getKey);
        String value = jsonObj.getString(getValue);
        GlobalContext.Put(key, value);
        return "Key=" + key + " Value=" + value;
    }

    /**
     * Just print ID of created item.
     */
    protected void printReturnedInformation(String itemType, String responseBody) {
        if (!responseBody.isEmpty()) {
            if (!itemType.isEmpty()) {
                JSONObject responseJson = new JSONObject(responseBody);
                if (responseJson.has("id")) {
                    System.out.println("ID of " + itemType + " CREATED/RETURNED by operation executed: " + responseJson.get("id").toString());
                }
            } else {
                System.out.println("GET response: " + responseBody);
            }
        }
    }

    public void closeClient() {
        client.close();
    }

    public class Method {
        public static final String GET = "GET";
        public static final String PUT = "PUT";
        public static final String POST = "POST";
    }
}
